package org.teremail.mailbox;

import java.util.List;

import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import net.sf.ehcache.config.CacheConfiguration;
import net.sf.ehcache.config.Configuration;

import org.teremail.mailbox.Folder.SortKey;

public class EhCacheMailboxDAO implements MailboxDAO {

    private static final String MAILBOX_CACHE_NAME = "mailbox";
    private final MailboxDAO mailboxDAO;
    private final Cache cache;
    private final CacheManager cacheManager;

    public EhCacheMailboxDAO(@Physical MailboxDAO mailboxDAO, int maxCacheElements, int timeToLive) {
        this.mailboxDAO = mailboxDAO;
        cache = new Cache(MAILBOX_CACHE_NAME, maxCacheElements, false, false, timeToLive, timeToLive);
        Configuration configuration = new Configuration();
        CacheConfiguration cacheConfiguration = new CacheConfiguration();
        cacheConfiguration.setOverflowToDisk(false);
        cacheConfiguration.setDiskPersistent(false);
        configuration.setDefaultCacheConfiguration(cacheConfiguration);
        cacheManager = new CacheManager(configuration);
        cacheManager.addCache(cache);
    }

    public void addMessage(FolderDO fdo, String messageId)
            throws FolderNotExistsException {
        mailboxDAO.addMessage(fdo, messageId);
    }

    public MailboxDO create(String user) throws MailboxExistsException {
        MailboxDO mailbox = mailboxDAO.create(user);
        cache.put(new Element(user, mailbox));
        return mailbox;
    }

    public MailboxDO findMailbox(String user) throws MailboxNotExistsException {
        
        Element element = cache.get(user);
        MailboxDO mailbox;
        
        if (element != null) {
            mailbox = (MailboxDO) element.getObjectValue();
        } else {
            mailbox = mailboxDAO.findMailbox(user);
            cache.put(new Element(user, mailbox));
        }
        
        return mailbox;
    }
    
    public FolderDO findFolder(MailboxDO mailbox, Path path)
            throws MailboxNotExistsException, FolderNotExistsException {
        String folderKey = getFolderKey(mailbox, path);
        Element element = cache.get(folderKey);
        
        FolderDO folder;
        if (element != null) {
            folder = (FolderDO) element.getObjectValue();
        } else {
            folder = mailboxDAO.findFolder(mailbox, path);
            cache.put(new Element(folderKey, folder));
        }
        
        return folder;
    }


    public List<EntryDO> getEntries(FolderDO fdo, int pageNo, int pageSize,
            SortKey... keys) {
        return mailboxDAO.getEntries(fdo, pageNo, pageSize, keys);
    }

    public List<EntryDO> getEntries(FolderDO f) {
        return mailboxDAO.getEntries(f);
    }

    public EntryDO getEntry(FolderDO folder, int uid)
            throws EntryNotExistsException {
        return mailboxDAO.getEntry(folder, uid);
    }

    public List<FolderDO> getFolders(MailboxDO mailbox, Path root) {
        return mailboxDAO.getFolders(mailbox, root);
    }

    public void remove(MailboxDO mailbox, Path path)
            throws MailboxNotExistsException, FolderNotExistsException {
        cache.remove(getFolderKey(mailbox, path));
        mailboxDAO.remove(mailbox, path);
    }

    public void renameFolder(MailboxDO mailbox, Path fromPath, Path toPath)
            throws MailboxNotExistsException, FolderExistsException,
            FolderNotExistsException {
        cache.remove(getFolderKey(mailbox, fromPath));
        mailboxDAO.renameFolder(mailbox, fromPath, toPath);
    }

    public FolderDO save(FolderDO inbox) throws FolderExistsException {
        FolderDO saved = mailboxDAO.save(inbox);
        String key = getFolderKey(saved);
        cache.put(new Element(key, saved));
        return saved;
    }

    private static String getFolderKey(FolderDO f) {
        return getFolderKey(f.getMailbox(), f.getPath());
    }
    
    private static String getFolderKey(MailboxDO m, Path p) {
        return m.getId() + ":" + p;
    }
    
    
    public void start() throws Exception {
    }

    public void stop() throws Exception {
    }
}